﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using ModelViewerAux;

namespace ModelViewer
{
    public partial class TreeViewer : UserControl
    {
        #region Interfaces

        /// <summary>
        /// Observers of the tree
        /// </summary>
        public interface IObserver
        {
            void UpdateObserver();
        }

        #endregion

        #region Embedded Classes

        private enum LabelTypes {Bones, Meshes, Animations}

        /// <summary>
        /// A class for tree nodes that include a ViewerModel
        /// </summary>
        private class ModelTreeNode : TreeNode
        {
            private ViewerModel model;

            public ViewerModel Model { get { return model; } }

            public ModelTreeNode(ViewerModel model)
                : base(model.Name)
            {
                this.model = model;
            }
        }

        /// <summary>
        /// A class for all tree nodes other than the ModelTreeNode.
        /// </summary>
        private abstract class ModelTreeChildNode : TreeNode
        {
            private ModelTreeNode modelNode;

            /// <summary>
            /// The tree node for the model this node is associated with
            /// </summary>
            public ModelTreeNode ModelTreeNode { get { return modelNode; } }

            public ModelTreeChildNode(string label, ModelTreeNode modelNode) : base(label)
            {
                this.modelNode = modelNode;
            }
        }
        
        /// <summary>
        /// A class for tree nodes that are labels
        /// </summary>
        private class LabelTreeNode : ModelTreeChildNode
        {
            private LabelTypes type;

            public LabelTypes Type { get { return type; } }


            public LabelTreeNode(string label, LabelTypes type, ModelTreeNode modelNode)
                : base(label, modelNode)
            {
                this.type = type;
            }
        }



        /// <summary>
        /// A class for tree nodes that include a bone
        /// </summary>
        private class BoneTreeNode : ModelTreeChildNode
        {
            private Bone bone;

            public Bone Bone { get { return bone; } }

            public BoneTreeNode(Bone bone, ModelTreeNode modelNode)
                : base(bone.Name, modelNode)
            {
                this.bone = bone;
            }
        }

        /// <summary>
        /// A class for tree nodes that include a mesh
        /// </summary>
        private class MeshTreeNode : ModelTreeChildNode
        {
            private Mesh mesh;

            public Mesh Mesh { get { return mesh; } }

            public MeshTreeNode(Mesh mesh, ModelTreeNode modelNode)
                : base(mesh.Name, modelNode)
            {
                this.mesh = mesh;
            }
        }

        /// <summary>
        /// A class for tree nodes that include an animation
        /// </summary>
        private class AnimTreeNode : ModelTreeChildNode
        {
            private AnimationClip clip;

            public AnimationClip Clip { get { return clip; } }

            private ClipPlayer player = null;

            public ClipPlayer Player { get { return player; } }

            public AnimTreeNode(ClipPlayer player, ModelTreeNode modelNode)
                : base(player.Clip.Name, modelNode)
            {
                this.player = player;
                this.clip = player.Clip;
            }
        }

        #endregion

        #region Fields

        /// <summary>
        /// This class needs to know all of the available models
        /// from the viewer.
        /// </summary>
        private List<ViewerModel> models = null;

        private ViewerModel selectedModel = null;

        private Bone selectedBone = null;

        private Mesh selectedMesh = null;

        private ClipPlayer selectedClipPlayer = null;

        private List<IObserver> observers = new List<IObserver>();

        private MainForm mainForm = null;

        #endregion

        #region Properties

        /// <summary>
        /// The currently selected model. This will be set for any node
        /// in the model that is selected.
        /// </summary>
        [Browsable(false)]
        public ViewerModel SelectedModel { get { return selectedModel; } }

        /// <summary>
        /// The currently selected bone. 
        /// </summary>
        [Browsable(false)]
        public Bone SelectedBone { get { return selectedBone; } }

        /// <summary>
        /// The currently selected mesh
        /// </summary>
        [Browsable(false)]
        public Mesh SelectedMesh { get { return selectedMesh; } }

        /// <summary>
        /// The currently selected animation clip's player
        /// </summary>
        [Browsable(false)]
        public ClipPlayer SelectedClipPlayer { get { return selectedClipPlayer; } }

        /// <summary>
        /// This class needs to know all of the avialable models from
        /// the viewer
        /// </summary>
        [Browsable(false)]
        public List<ViewerModel> Models
        {
            get { return models; }
            set { models = value; }
        }

        #endregion

        #region Construction and Initialization

        public TreeViewer()
        {
            InitializeComponent();

            UpdateUI();
        }

        [Browsable(false)]
        public MainForm MainForm
        {
            get
            {
                return mainForm;
            }
            set
            {
                mainForm = value;
                if (value != null)
                {
                    MenuRenderer renderer = new MenuRenderer(mainForm.ColorTable);
                    renderer.TreeViewer = true;
                    menuStrip1.Renderer = renderer;
                }
            }
        }

        #endregion

        #region Observers

        public void AddObserver(IObserver observer)
        {
            observers.Add(observer);
        }

        public void RemoveObserver(IObserver observer)
        {
            observers.Remove(observer);
        }

        public void UpdateAllObservers()
        {
            foreach (IObserver observer in observers)
            {
                observer.UpdateObserver();
            }
        }

        #endregion


        #region Building the tree

        /// <summary>
        /// Build the tree for the viewer. This rebuilds from scratch, destroying any
        /// previous tree.
        /// </summary>
        public void Build()
        {
            treeView.BeginUpdate();
            treeView.Nodes.Clear();

            if (Models.Count == 0)
            {
                // No models, so we can't load
                treeView.EndUpdate();
                return;
            }

            // Loop over the models
            foreach (ViewerModel model in Models)
            {
                //
                // Add the model to the tree
                //

                ModelTreeNode modelNode = new ModelTreeNode(model);
                treeView.Nodes.Add(modelNode);

                //
                // Add the bones to the tree
                //

                List<Bone> bones = model.Bones;
                if(bones.Count > 0)
                {
                    Dictionary<Bone, BoneTreeNode> boneToNode = new Dictionary<Bone, BoneTreeNode>();
                    LabelTreeNode label = new LabelTreeNode("Bones", LabelTypes.Bones, modelNode);
                    modelNode.Nodes.Add(label);

                    BoneTreeNode root = new BoneTreeNode(bones[0], modelNode);
                    label.Nodes.Add(root);
                    boneToNode[bones[0]] = root;

                    for (int i = 1; i < bones.Count; i++)
                    {
                        BoneTreeNode node = new BoneTreeNode(bones[i], modelNode);
                        boneToNode[bones[i]] = node;

                        TreeNode parentNode = boneToNode[bones[i].Parent];
                        parentNode.Nodes.Add(node);
                    }

                }

                //
                // Add the meshes to the tree
                //

                List<Mesh> meshes = model.Meshes;
                if (meshes.Count > 0)
                {
                    LabelTreeNode label = new LabelTreeNode("Meshes", LabelTypes.Meshes, modelNode);
                    modelNode.Nodes.Add(label);

                    foreach (Mesh mesh in meshes)
                    {
                        MeshTreeNode node = new MeshTreeNode(mesh, modelNode);
                        label.Nodes.Add(node);
                    }
                }

                //
                // Add the animation players to the tree
                //

                List<ClipPlayer> players = model.ClipPlayers;
                if (players.Count > 0)
                {
                    LabelTreeNode label = new LabelTreeNode("Clips", LabelTypes.Animations, modelNode);
                    modelNode.Nodes.Add(label);

                    foreach (ClipPlayer player in players)
                    {
                        AnimTreeNode node = new AnimTreeNode(player, modelNode);
                        label.Nodes.Add(node);
                    }
                }

            }

            treeView.EndUpdate();
            UpdateSelection();
        }

        #endregion

        #region Selection Management

        /// <summary>
        /// This is called to update the current selected item information.
        /// </summary>
        private void UpdateSelection()
        {
            // Until we know otherwise
            ViewerModel selectModel = null;
            Bone selectBone = null;
            Mesh selectMesh = null;
            ClipPlayer selectClipPlayer = null;

            if (treeView.SelectedNode != null)
            {
                TreeNode node = treeView.SelectedNode;
                if (node is ModelTreeNode)
                {
                    // We have a selected tree node that is a model node
                    selectModel = ((ModelTreeNode)node).Model;
                }

                if (node is ModelTreeChildNode)
                {
                    selectModel = ((ModelTreeChildNode)node).ModelTreeNode.Model;
                }

                if (node is BoneTreeNode)
                {
                    selectBone = ((BoneTreeNode)node).Bone;
                }

                if (node is MeshTreeNode)
                {
                    selectMesh = ((MeshTreeNode)node).Mesh;
                }

                if (node is AnimTreeNode)
                {
                    selectClipPlayer = ((AnimTreeNode)node).Player;
                }
            }
            
            // Make these the selection if there is a change
            if (selectModel != selectedModel || 
                selectBone != selectedBone || 
                selectMesh != selectedMesh ||
                selectClipPlayer != selectedClipPlayer)
            {
                selectedModel = selectModel;
                selectedBone = selectBone;
                selectedMesh = selectMesh;
                selectedClipPlayer = selectClipPlayer;

                UpdateAllObservers();
            }
                
            UpdateUI();
        }

        #endregion

        #region User Interface

        /// <summary>
        /// Update the user interface to match the current selection
        /// </summary>
        private void UpdateUI()
        {
            bool empty = treeView.Nodes.Count == 0;
            bool anyNode = treeView.SelectedNode != null;

            expandAllToolStripMenuItem.Enabled = !empty;
            collapseAllToolStripMenuItem.Enabled = !empty;
            expandSelectedAllToolStripMenuItem.Enabled = anyNode;
            collapsedSelectedAllToolStripMenuItem.Enabled = anyNode && treeView.SelectedNode.IsExpanded;
        }

        private void expandAllToolStripMenuItem_Click(object sender, EventArgs e)
        {
            treeView.ExpandAll();
            if(treeView.Nodes.Count > 0)
                treeView.TopNode = treeView.Nodes[0];
            
        }

        private void collapseAllToolStripMenuItem_Click(object sender, EventArgs e)
        {
            treeView.CollapseAll();
        }

        private void OnAfterSelect(object sender, TreeViewEventArgs e)
        {
            UpdateSelection();
        }


        private void expandSelectedAllToolStripMenuItem_Click(object sender, EventArgs e)
        {
            TreeNode node = treeView.SelectedNode;
            if (node != null)
                node.ExpandAll();
        }

        private void collapsedSelectedAllToolStripMenuItem_Click(object sender, EventArgs e)
        {
            TreeNode node = treeView.SelectedNode;
            if (node != null)
                node.Collapse();
        }

        #endregion

    }
}
